/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.api.repository.ITypeTransProcesser;
import com.inspur.edp.cef.designtime.api.IFieldCollection;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspAssociationKey;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.entity.condition.FilterCondition;
import com.inspur.edp.cef.entity.condition.SortCondition;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.DotNetToJavaStringHelper;
import com.inspur.edp.cef.generator.coreGenerator.base.CefCoreGeneratorContext;
import com.inspur.edp.cef.generator.overall.CefGeneratorContext;
import com.inspur.edp.cef.generator.overall.Utils;
import com.inspur.edp.cef.repository.assembler.LogicDeleteInfo;
import com.inspur.edp.cef.repository.assembler.entity.MainEntityAssembler;
import com.inspur.edp.cef.repository.typetransprocesser.*;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonModel;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.lcm.databaseobject.api.DatabaseObjectServiceForWebIde;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import com.inspur.edp.udt.designtime.api.entity.element.UdtElement;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.databaseobject.api.entity.DataType;
import io.iec.edp.caf.databaseobject.api.entity.DatabaseObjectColumn;
import io.iec.edp.caf.databaseobject.api.entity.DatabaseObjectTableCore;
import lombok.var;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;

public class CefRepoAssembler extends MainEntityAssembler {
    private IGspCommonObject gspCommonObject;
    private DatabaseObjectServiceForWebIde dboService;
    private CefCoreGeneratorContext cefCoreGeneratorContext;

    public CefRepoAssembler(IGspCommonObject gspCommonObject, CefCoreGeneratorContext cefCoreGeneratorContext){
        this.gspCommonObject = gspCommonObject;
        this.cefCoreGeneratorContext = cefCoreGeneratorContext;
//        initAssociationInfos();
        initColumnInfos();
    }

    //todo wangmj path路径待验证
    @Override
    protected String getTableName() {
        if(StringUtils.isEmpty(getBeObject().getRefObjectName())){
            throw new RuntimeException(getBeObject() + "的dbo(数据库对象)未生成");
        }
        DatabaseObjectTableCore dbo =
                (DatabaseObjectTableCore)
                        getDboService()
                                .getDatabaseObjectById(
                                        cefCoreGeneratorContext.getCefGenContext().getMetadataPath(),
                                        getBeObject().getRefObjectName());
        if(dbo == null){
            throw new RuntimeException(getBeObject() + "的dbo(数据库对象)不存在");
        }
        return dbo.getCode();
    }

    @Override
    protected String getTableAlias() {
        return this.gspCommonObject.getCode();
    }

    @Override
    protected boolean getIsFiscalTable() {
        return false;
    }

    @Override
    protected String getDboId() {
        return getBeObject().getRefObjectName();
    }

    @Override
    public boolean getUseDataCache(){
        return getBeObject().getBelongModel().getEnableCaching();
    }

    @Override
    public String getCacheConfigID(){
        return getBeObject().getBelongModel().getCacheConfiguration();
    }

    @Override
    public LogicDeleteInfo getLogicDeleteInfo(){
        GspBizEntityObject gspBizEntityObject = (GspBizEntityObject)gspCommonObject;
        if (gspBizEntityObject.getLogicDeleteControlInfo() != null && gspBizEntityObject
                .getLogicDeleteControlInfo().getEnableLogicDelete()) {
            for (IGspCommonField gspCommonField : gspCommonObject.getContainElements()) {
                if (gspCommonField.getID().equals(
                        gspBizEntityObject.getLogicDeleteControlInfo().getLogicDeleteControlElementId())) {
                    return new LogicDeleteInfo(true, gspCommonField.getLabelID());
                }
            }
        }
        return new LogicDeleteInfo(false,"");
    }

    @Override
    public String getNodeCode() {
        return this.gspCommonObject.getCode();
    }

    @Override
    public MainEntityAssembler getParentAssembler() {
        if(this.gspCommonObject.getParentObject() == null){
            return null;
        }
        else {
         return new CefRepoAssembler(this.gspCommonObject.getParentObject(), cefCoreGeneratorContext);
        }
    }

    @Override
    public void initColumnInfo() {

    }

    public void initColumnInfos() {
        IFieldCollection elementList = this.gspCommonObject.getContainElements();
        DatabaseObjectTableCore dbo = this.getDatabaseObject();
        if(dbo == null)
            throw new RuntimeException("未找到" + this.gspCommonObject.getCode() + "对应的dbo信息,请确认dbo文件是否存在!");
        for (IGspCommonField element : elementList) {
            if (element.getIsVirtual() || element.getObjectType() == GspElementObjectType.DynamicProp) {
                continue;
            }
            if (element.getIsUdt()) {
                UnifiedDataTypeDef udt = (UnifiedDataTypeDef)cefCoreGeneratorContext.getCefGenContext().getMetadataJitContext().getMetadata(element.getUdtID()).getContent();
                if (udt instanceof SimpleDataTypeDef || (udt instanceof ComplexDataTypeDef && ((ComplexDataTypeDef)udt).getDbInfo().getMappingType() == ColumnMapType.SingleColumn)){
                    var column = dbo.getColumnById(((IGspCommonElement)element).getColumnID());
                    if (column == null) {
                        throw new RuntimeException("dbo中找不到" + element.getLabelID() + "对应的列");
                    }
                    addColumn((IGspCommonElement) element, (IGspCommonElement) element,column, false, null);
                    continue;
                }
                for (var childElement : element.getChildElements()) {
                    var childEle =
                            (IGspCommonElement) ((childElement instanceof IGspCommonElement) ? childElement : null);

                    var column = dbo.getColumnById(childEle.getColumnID());
                    if (column == null) {
                        throw new RuntimeException("dbo中找不到" + childEle.getLabelID() + "对应的列");
                    }
                    // TODO 暂时没法只根据element判断是否是ChildElement
                    childEle.setIsUdt(true);
                    addChildColumn((IGspCommonElement) childEle, (IGspCommonElement) element,column, (ComplexDataTypeDef) udt);
                }
            } else {
                var column = dbo.getColumnById(((IGspCommonElement) element).getColumnID());
                if (column == null) {
                    throw new RuntimeException("dbo中找不到" + element.getLabelID() + "对应的列");
                }
                addColumn((IGspCommonElement) element, (IGspCommonElement) element,column, false, null);
            }
        }
    }

    private void addChildColumn(IGspCommonElement element, IGspCommonElement belongElement, DatabaseObjectColumn dboColumn,ComplexDataTypeDef complexUdt){
        UdtElement udtElement=(UdtElement) complexUdt.findElement(belongElement.getMappingRelation().getMappingInfo(element.getID()));
        if(udtElement==null){
            throw new RuntimeException("没有找到多值存为多列udt上"+element.getLabelID()+"的字段！");
        }
        addColumn(element, belongElement, dboColumn, true, udtElement) ;
    }

    private void addColumn(IGspCommonElement element, IGspCommonElement belongElement, DatabaseObjectColumn dboColumn, boolean isMultiColumns, UdtElement field){
        ITypeTransProcesser paramProcesser = getTypeProcessor(element, dboColumn , isMultiColumns, field);
        super.addColumn(element.getLabelID(), element.getColumnID(), paramProcesser, isPrimaryKey(element), element.getIsMultiLanguage(), element.getIsUdt(), element.getObjectType() == GspElementObjectType.Association, element.getObjectType() == GspElementObjectType.Enum,belongElement == null ? "" : belongElement.getLabelID(), isParentColumn(element));
    }

    private boolean isPrimaryKey(IGspCommonField element){
        return this.gspCommonObject.getColumnGenerateID().getElementID().equals(element.getID());
    }

    private boolean isParentColumn(IGspCommonElement element) {
        if (element.getBelongObject() != null && element.getBelongObject().getParentObject() != null) {
            return element.getBelongObject().getKeys().get(0).getSourceElement() .equals( element.getID());
        }
        return false;
    }

    private ITypeTransProcesser getEnumTypeProcesser(MediateType enumType) {
//        Class cl = null;
//        try {
//            cl = Class.forName(enumType.getFullName());
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }
        return null;
    }

    private ITypeTransProcesser getTypeProcessor(IGspCommonElement element, DatabaseObjectColumn dboColumn, boolean isMultiColumns, UdtElement field){
        ITypeTransProcesser paramProcesser = null;
        var dataType = transDataType(dboColumn.getDataType());
        if (isMultiColumns && field.getObjectType() == GspElementObjectType.Enum) {
            MediateType enumType = new MediateType(
                    field.getBelongObject().getGeneratedEntityClassInfo().getClassNamespace(),
                    field.getEnumTypeName());
            paramProcesser = getEnumTypeProcesser(enumType);
//        } else if (element.getObjectType() == GspElementObjectType.Enum) {
//            MediateType enumType = new MediateType(
//                    element.getBelongObject().getGeneratedEntityClassInfo().getClassNamespace(),
//                    element.getEnumTypeName());
//            paramProcesser = getEnumTypeProcesser(enumType);
        } else {
            paramProcesser = getTypeProcesser(dataType, element);
        }
        return paramProcesser;
    }

    private ITypeTransProcesser getTypeProcesser(
            GspDbDataType dbType, IGspCommonElement element) // GSPElementDataType elementType)
    {
        switch (element.getMDataType()) {
            case Boolean:
                if (dbType == GspDbDataType.Int) {
                    return Bool2IntProcesser.getInstacne();
                }
                if (dbType == GspDbDataType.Char) {
                    return Bool2CharProcesser.getInstacne();
                }
                if(dbType == GspDbDataType.Boolean) {
                    return BoolTransProcesser.getInstacne();
                }
                break;
            case Date:
            case DateTime:
                return DateTimeTransProcesser.getInstacne();
            case Decimal:
                if (dbType == GspDbDataType.Decimal) {
                    return DecimalTransProcesser.getInstacne();
                }
                break;
            case Integer:
                if (dbType == GspDbDataType.Int) {
                    return IntTransProcesser.getInstacne();
                }
                break;
            case String:
                return VarcharTransProcesser.getInstacne();
            case Text:
                return ClobTransProcesser.getInstacne();
            case Binary:
                return BinaryTransProcesser.getInstacne();
        }

        throw new RuntimeException(
                "#GSPBefError#"
                        + "没有找到字段类型为"
                        + element.getMDataType().toString()
                        + "，数据库类型为"
                        + dbType.toString()
                        + "的转换器"
                        + "#GSPBefError#");
    }

    private GspDbDataType transDataType(DataType dataType) {
        GspDbDataType dbDataType = GspDbDataType.VarChar;
        switch (dataType) {
            case Char:
                dbDataType = GspDbDataType.Char;
                break;
            case Varchar:
                dbDataType = GspDbDataType.VarChar;
                break;
            case Blob:
                dbDataType = GspDbDataType.Blob;
                break;
            case DateTime:
                dbDataType = GspDbDataType.DateTime;
                break;
            case TimeStamp:
                dbDataType = GspDbDataType.DateTime;
                break;
            case Clob:
                dbDataType = GspDbDataType.Clob;
                break;
            case Int:
            case SmallInt:
                dbDataType = GspDbDataType.Int;
                break;
            case Decimal:
                dbDataType = GspDbDataType.Decimal;
                break;
            case NChar:
                dbDataType = GspDbDataType.NChar;
                break;
            case NVarchar:
                dbDataType = GspDbDataType.NVarChar;
                break;
            case NClob:
                dbDataType = GspDbDataType.NClob;
                break;
            case Boolean:
                dbDataType = GspDbDataType.Boolean;
                break;
        }
        return dbDataType;
    }


    public void initAssociationInfos(){
        for (IGspCommonField field : this.gspCommonObject.getContainElements()) {
            if (field.getHasAssociation()){
                if (field.getChildAssociations() == null || field.getChildAssociations().size() < 1) {
                    continue;
                }
                for (GspAssociation association : field.getChildAssociations()) {
                    addAssociationInfo(association);
                }
            }
        }
    }

    private void addAssociationInfo(GspAssociation association){
        HashMap<String,String> refColumns=new HashMap<String,String>();
        GspMetadata metadata =cefCoreGeneratorContext.getCefGenContext().getMetadataJitContext().getMetadata(association.getRefModelID());
        // GspMetadata metadata = CefGenServiceManager.getLcmDtService().getRefMetadata(association.getRefModelID());
        IGspCommonModel refModel = (IGspCommonModel) ((metadata.getContent() instanceof IGspCommonModel) ? metadata.getContent() : null);
        DataValidator.checkForNullReference(refModel, "RefModel");
        IGspCommonField belongElement = association.getBelongElement();
        String sourceId = getSourceElementId(association);
        if (DotNetToJavaStringHelper.isNullOrEmpty(sourceId)) {
            throw new RuntimeException("#GSPBefError#" + "关联字段【" + belongElement.getName() + "】缺少关联关系" + "#GSPBefError#");
        }
        for (IGspCommonField refElement : association.getRefElementCollection()) {
            IGspCommonElement targetElement = refModel.findElementById(refElement.getRefElementId());
            if(targetElement == null){
                throw new RuntimeException("没有获取到关联带出字段的引用字段，当前实体："+this.gspCommonObject.getCode()+"，关联实体："
                        +refModel.getCode()+",字段标签："+refElement.getLabelID());
            }
            refColumns.put(refElement.getLabelID(), targetElement.getLabelID());
        }

        IGspCommonObject targetObject = null;
        if (DotNetToJavaStringHelper.isNullOrEmpty(association.getRefObjectID())) {
            targetObject = refModel.getMainObject();
        } else {
            targetObject = getObjectByID(refModel, association.getRefObjectID());
        }
        if (targetObject == null) {
            throw new RuntimeException("找不到实体节点");
        }

        IGspCommonElement sourceElement = targetObject.findElement(sourceId);
        CefGeneratorContext cefContext = cefCoreGeneratorContext.getCefGenContext();
        TypeInfo assDataType = getTypeInfo(belongElement, cefContext);
        Class assClass = null;
        try {
            assClass = Class.forName(assDataType.getTypeFullName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        super.addAssociationInfo(refModel.getGeneratedConfigID(), targetObject.getCode(), belongElement.getLabelID(), sourceElement.getLabelID(), assClass, refColumns);
    }

    private String getSourceElementId(GspAssociation association) {
        IGspCommonField belongElement = association.getBelongElement();
        for (GspAssociationKey associationKey : association.getKeyCollection()) {
            if (associationKey.getTargetElement() .equals(belongElement.getID())) {
                return associationKey.getSourceElement();
            }
        }
        return null;
    }

    private IGspCommonObject getObjectByID(IGspCommonModel model, String objectID) {

        for (IGspCommonObject item : model.getAllObjectList()) {
            if (objectID.equals(item.getID())) {
                return item;
            }
        }
        return null;
    }

    private TypeInfo getTypeInfo(IGspCommonField belongElement, CefGeneratorContext cefContext){
        TypeInfo assDataType;
        if (belongElement.getIsUdt())
            assDataType = Utils.getUdtType(belongElement.getUdtPkgName(), belongElement.getUdtID(), cefContext);
        else {
            assDataType = new TypeInfo(Utils.getAssociationTypeName(belongElement));
            assDataType.setTypePackageName(belongElement.getBelongObject().getGeneratedEntityClassInfo().getClassNamespace());
//        assDataType.setTypePackageName(belongElement.getBelongObject());
        }
        return assDataType;
    }

    @Override
    protected ArrayList<FilterCondition> getDefaultFilterCondition() {
        String filterCondition = this.gspCommonObject.getFilterCondition();
        if (DotNetToJavaStringHelper.isNullOrEmpty(filterCondition)) {
            return null;
        }
        else{
            //todo wangmj 待更新
            return null;
        }
    }

    @Override
    protected ArrayList<SortCondition> getDefaultSortCondition() {
        String orderbyCondition = this.gspCommonObject.getOrderbyCondition();
        if (DotNetToJavaStringHelper.isNullOrEmpty(orderbyCondition)) {
            return null;
        }
        else{
            return null;
        }
    }

    private GspBizEntityObject getBeObject() {
        return (GspBizEntityObject) gspCommonObject;
    }

    private DatabaseObjectServiceForWebIde getDboService() {
        if (dboService == null) {
            dboService = SpringBeanUtils.getBean(DatabaseObjectServiceForWebIde.class);
        }
        return dboService;
    }

    protected DatabaseObjectTableCore getDatabaseObject() {
        return (DatabaseObjectTableCore)
                getDboService()
                        .getDatabaseObjectById(
                                cefCoreGeneratorContext.getCefGenContext().getMetadataPath(),
                                getBeObject().getRefObjectName());
    }
}
